/**
 * js中的垃圾
 *  + js中内存管理是自动的
 *  + 对象不再被引用时是垃圾
 *  + 对象不能从根上访问到时是垃圾
 * 
 * js中的可达对象
 *  + 可以访问到的对象就是可达对象（引用、作用域链）
 *  + 可达的标准就是从根出发是否能够被找到
 *  + js中的根就可以理解为是全局变量对象
 */

let obj = { name: 'xm' } // obj引用了内存中的对象
let ali = obj // ali也引用了内存中的对象
obj = null // obj被释放，但是ali还引用者对象
console.log(ali)

function objGroup (obj1, obj2) {
  obj1.next = obj2
  obj2.prev = obj1
  return {
    o1: obj1,
    o2: obj2,
  }
}
const r = objGroup({ name: 'obj1' }, { name: 'obj2' })
console.log(r)
delete r.o1 // 清除r对o1的引用
console.log(r)
delete r.o2.prev // 清除对o1的引用
console.log(r)

/**
 * GC定义与作用
 *  + GC就是垃圾回收机制的简写
 *  + GC可以找到内存中的垃圾、并释放和回收空间
 * 
 * GC算法是什么
 *  + GC是一种机制，垃圾回收器完成具体工作
 *  + 工作的内容就是查找垃圾释放空间、回收空间
 *  + 算法就是工作时查找和回收所有遵循的规则
 * 
 * 常见的GC算法
 *  + 引用计数
 *  + 标记清除
 *  + 标记整理
 *  + 分代回收
 */

// gc里的垃圾是什么
// 程序中不再需要使用的对象
function func1 () {
  name = 'zc'
  return `my name is ${name}`
}

// 程序中不能再访问到的对象，const
function func2 () {
  const name = 'zc'
  return `my name is ${name}`
}

/**
 * 引用计数算法
 *  + 核心思想：设置引用数，判断当前引用数是否为0
 *  + 引用计数器
 *  + 引用关系改变时修改引用数字
 *  + 引用数字为0时立即回收
 */

const user1 = { age: 11 }
const user2 = { age: 22 }
const user3 = { age: 33 }
const nameList = [ user1.age, user2.age, user3.age ] // 引用里三个user里面的内存空间，user内部计数不为0

function fn() {
  num1 = 1 // fn执行完毕后，此时变量由于声明在全局上，全局对其还由引用，计数不为0
  num2 = 2 // fn执行完毕后，此时变量由于声明在全局上，全局对其还由引用，计数不为0
  const num3 = 3 // fn执行完毕后，此时变量作用域由于在函数内部，没有其他引用了，计数为0
}
fn()

/**
 * 引用计数算法的优点
 *  + 发现垃圾时立即回收，可以即时回收垃圾对象
 *  + 最大限度减少程序暂停，减少程序卡顿
 * 引用计数算法的缺点
 *  + 无法回收循环引用的对象
 *  + 资源消耗大，时间开销大，需要随时监听数据是否被修改，随时修改数据的引用数
 */

function loopFn () {
  const obj1 = {}
  const obj2 = {}
  obj1.name = obj2
  obj2.name = obj1
  return 'nothing'
}
loopFn() // 当函数执行完毕后，obj1和obj2应该立即被删除，但是函数内部obj1和obj2形成了循环引用，造成引用计数都不为0无法被回收

/**
 * 标记清除算法
 *  + 核心思想：分标记和清除两个阶段完成
 *  + 遍历所有对象找到标记活动对象
 *  + 遍历所有对象清除没有标记的对象，并把第一阶段被标记的对象的标记抹掉，便于下一轮重新做标记
 *  + 回收相应的空间（回收的空间会放在一个叫空闲列表的地方，方便后续程序直接在这里申请空间使用）
 * 标记清除算法优缺点
 *  + 优点：可以解决引用计数中的循环引用无法被回收的情况
 *  + 缺点：
 *    + 根下有三对象，a：占据2个空间，b：占据3个空间，c：占据1个空间，当a和c被回收到空闲列表中时，由于中间隔了一个b，所以a和c的内存地址不会连续，会形成分散的碎片化地址，如果新对象是1.5个空间，则不会利用刚刚被回收的地址，而是新创建一个内存地址，如果新对象刚好是2个或1个空间，则会利用刚才被回收的a或者c内存地址
 *    + 不能立即回收垃圾对象，会等到最后才执行清除，清除时程序是暂停的
 * 标记整理算法（v8引擎中配合标记清除算法使用）
 *  + 标记整理可以看做是标记清除的增强
 *  + 标记阶段的操作和标记清除一致
 *  + 清除阶段会先执行整理，先移动对象位置，将活动对象放在一起，地址上形成连续，回收剩余的非活动地址，这样就不会形成碎片化的内存地址
 * 标记整理算法优缺点
 *  + 优点：减少碎片化空间
 *  + 缺点：不会立即回收垃圾对象
 */